//  Copyright (C) 2002 Ultr@VNC Team Members. All Rights Reserved.
//  Copyright (C) 2000-2002 Const Kaplinsky. All Rights Reserved.
//  Copyright (C) 2002 RealVNC Ltd. All Rights Reserved.
//  Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
//
//  This file is part of the VNC system.
//
//  The VNC system is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
//  USA.
//
// If the source code for the VNC system is not available from the place 
// whence you received this file, check http://www.uk.research.att.com/vnc or contact
// the authors on vnc@uk.research.att.com for information on obtaining it.

// vncDesktop implementation

// System headers
#include <assert.h>
#include "stdhdrs.h"

// Custom headers
//#include <WinAble.h>
#include <omnithread.h>
#include "WinVNC.h"
#include "VNCHooks\VNCHooks.h"
#include "vncServer.h"
#include "vncKeymap.h"
#include "rfbRegion.h"
#include "rfbRect.h"
#include "vncDesktop.h"
#include "vncService.h"
// Modif rdv@2002 - v1.1.x - videodriver
#include "vncOSVersion.h"

#include "mmSystem.h" // sf@2002
#include "TextChat.h" // sf@2002
#include "vncdesktopthread.h"




// Constants
const UINT RFB_SCREEN_UPDATE = RegisterWindowMessage("WinVNC.Update.DrawRect");
const UINT RFB_COPYRECT_UPDATE = RegisterWindowMessage("WinVNC.Update.CopyRect");
const UINT RFB_MOUSE_UPDATE = RegisterWindowMessage("WinVNC.Update.Mouse");
const char szDesktopSink[] = "WinVNC desktop sink";

extern int counterwatch;//global var for driverwatch
bool g_Desktop_running;
extern bool g_DesktopThread_running;
extern bool g_update_triggered;
DWORD WINAPI BlackWindow(LPVOID lpParam);

//
// // Modif sf@2002 - v1.1.0 - Optimization
//
// Here we try to speed up the Poll FullScreen function.
// Instead of adding the entire Rect to cached region,
// we divide it in 32/32 pixels blocks and only scan the top-left
// pixel of each block.
// If a pixel has changed, we find the smallest window containing this pixel
// and add the Window rect to the cached region
//
// This function is supposed to be a good compromise between
// speed and efficiency.
// The accuracy has been greatly improved and it now detects very
// small changes on screen that are not detected by the Hooks.
// This new accuracy is obtained by shifting the detection grid 
// in diagonale (the function works with several grids that are tested 
// alternatively). There's probably still room for improvements...
//
// NB: Changes generated by applications using hardware acceleration 
// won't be detected (In order to see a video played on the server using
// Media Player, the vncviewer user must desactivate "Hardware accelation"
// in the Media Player options). 
// 
// 
//
void
vncDesktop::FastDetectChanges(rfb::Region2D &rgn, rfb::Rect &rect, int nZone, bool fTurbo)
{
	RGBPixelList::iterator iPixelColor;
	RGBPixelList *pThePixelGrid;
	bool fInitGrid = false;
	bool fIncCycle = false;
	// For more accuracy, we could use 24 or even 16
	const int PIXEL_BLOCK_SIZE  = 32; // Pixels Grid definition
	const int GRID_OFFSET = 4;  // Pixels Grid shifting during cycle (in pixels)
								// The number of grid per zone is PIXEL_BLOCK_SIZE/GRID_OFFSET (must be int !)
	int x, y;
	int xo, yo;
	// WindowsList lWList;
	WindowsList::iterator iWindow;

	// In turbo mode, we clear the list of windows at each iteration -> Lot of updates because 
	//  the same windows can be detected several times during a complete cycle
	// (To avoid this pb, we must update all the grids at the same coordinate when a pixel of 
	// one grid has changed -> later)
	// Otherwise we only clear it each time the Grid cycle loops -> Less updates, less framerate,
	// less CPU, less bandwidth
	if (fTurbo)
		m_lWList.clear();
	else if (m_nGridCycle == 0)
		m_lWList.clear();

	// Create all the Grids (for all the 5 zones (4 quarter screens + 1 full screen)
	// Actually, the quarter screens are not utilized in v1.1.0 but it does not
	// generate any overhead neither additionnal memory consumption (see below)
	if (m_lGridsList.empty())
	{
		for (int i = 0; i < (5 * PIXEL_BLOCK_SIZE / GRID_OFFSET); i++)
		{
			RGBPixelList *pList = new RGBPixelList;
			if (pList != NULL)
			{
				m_lGridsList.push_back(pList);
			    vnclog.Print(LL_INTINFO, VNCLOG("### PixelsGrid %d created !\n"), i);
			}
		}
	}

	// We test one zone at a time 
	// vnclog.Print(LL_INTINFO, VNCLOG("### Polling Grid %d - SubGrid %d\n"), nZone, m_nGridCycle); 
	GridsList::iterator iGrid;
	int nIndex = 0;
	int nGridPos = (nZone * PIXEL_BLOCK_SIZE / GRID_OFFSET) + m_nGridCycle;

	iGrid = m_lGridsList.begin();
	while (nIndex != nGridPos)
	{
		iGrid++;
		nIndex++;
	}

	iPixelColor = ((RGBPixelList*)(*iGrid))->begin();
	pThePixelGrid = (RGBPixelList*) *iGrid;

	if (nZone == 0 || nZone == 4)
	{
	   // vnclog.Print(LL_INTINFO, VNCLOG("### IncCycle Please !\n")); 
	   fIncCycle = true;
	}

	if (pThePixelGrid->empty())
	{
		// vnclog.Print(LL_INTINFO, VNCLOG("### PixelsGrid Init\n"));
		fInitGrid = true;
	}

	int nOffset = GRID_OFFSET * m_nGridCycle;

	// We walk our way through the Grids
	for (y = rect.tl.y; y < rect.br.y; y += PIXEL_BLOCK_SIZE)
	{
		yo = y + nOffset;

		for (x = rect.tl.x; x < rect.br.x; x += PIXEL_BLOCK_SIZE)
		{
			xo = x + nOffset;

			// If init list
			if (fInitGrid)
			{
			   COLORREF PixelColor = GetPixel(m_hrootdc, xo, yo);
			   pThePixelGrid->push_back(PixelColor);
			   // vnclog.Print(LL_INTINFO, VNCLOG("### PixelsGrid Init : Pixel xo=%d - yo=%d - C=%ld\n"), xo, yo, (long)PixelColor); 
			   continue;
			}

			// Read the pixel's color on the screen
		    COLORREF PixelColor = GetPixel(m_hrootdc, xo, yo);

			// If the pixel has changed
			if (*iPixelColor != PixelColor)
			{
				// Save the new Pixel in the list
				*iPixelColor = PixelColor;

				// Then find the corresponding Window
				POINT point;
				RECT rect;

				point.x = xo;
				point.y = yo;

				// Find the smallest, non-hidden, non-disabled Window containing this pixel
				// REM: We don't use ChildWindowFromPoint because we don't want of hidden windows
				HWND hDeskWnd = GetDesktopWindow();
				HWND hwnd = WindowFromPoint(point);
				bool fAlready = false;

				// Look if we've already detected this window
				for (iWindow = m_lWList.begin(); iWindow != m_lWList.end(); iWindow++)
				{
					if (*iWindow == hwnd)
					{
						fAlready = true;
						break;
					}
				}
				
				// Add the corresponding rect to the cache region 
				if (!fAlready && (hwnd != hDeskWnd) && GetWindowRect(hwnd, &rect))
				{
					//Buffer coordinates
					rect.left-=m_ScreenOffsetx;
					rect.right-=m_ScreenOffsetx;
					rect.top-=m_ScreenOffsety;
					rect.bottom-=m_ScreenOffsety;
					rfb::Rect wrect = rfb::Rect(rect).intersect(m_Cliprect);
					if (!wrect.is_empty())
					{
						rgn = rgn.union_(wrect);
						m_lWList.push_back(hwnd);
					}
					// char szName[64];
					// GetWindowText(hwnd, szName, 64);
					// vnclog.Print(LL_INTINFO, VNCLOG("### Changed Window : %s (at x=%d - y=%d)\n"), szName, x, y); 
					// return;
				}
			}

			iPixelColor++; // Next PixelColor in the list
		}
	}

	if (fIncCycle)
	{
	   m_nGridCycle = (m_nGridCycle + 1) % (PIXEL_BLOCK_SIZE / GRID_OFFSET);
	}

}




// Implementation

vncDesktop::vncDesktop()
{
	m_thread = NULL;

	m_hwnd = NULL;
	m_timerid = 0;
	m_hnextviewer = NULL;
	m_hcursor = NULL;
	m_hOldcursor = NULL; // sf@2002

	m_displaychanged = FALSE;
	m_update_triggered = FALSE;
	g_update_triggered = FALSE;

	m_hrootdc = NULL;
	m_hmemdc = NULL;
	m_membitmap = NULL;

	m_initialClipBoardSeen = FALSE;

	m_foreground_window = NULL;

	// Vars for Will Dean's DIBsection patch
	m_DIBbits = NULL;
	m_formatmunged = FALSE;

	m_clipboard_active = FALSE;

	m_pollingcycle = 0;

	// Modif sf@2002 - v1.1.0 
	m_lWList.clear();
    m_lGridsList.clear();
	m_nGridCycle = 0;

	// Modif sf@2002 - v1.1.0
	// m_lLastTempo = 0L;
	m_lLastMouseUpdateTime = 0L;
	m_lLastSlowClientTestTime = timeGetTime();

	// sf@2002 - TextChat - No more used for now
	// m_fTextChatRunning = false;
	// m_pCurrentTextChat = NULL;

	// Modif rdv@2002 - v1.1.x - videodriver
#ifdef DRIVER
	m_videodriver=NULL;
#endif
	m_ScreenOffsetx=0;
	m_ScreenOffsety=0;
	m_hookdriver=false;

	OldPowerOffTimeout=0;
	hModule=NULL;
	char szCurrentDir[MAX_PATH];
		if (GetModuleFileName(NULL, szCurrentDir, MAX_PATH))
		{
			char* p = strrchr(szCurrentDir, '\\');
			if (p == NULL) return;
			*p = '\0';
			strcat (szCurrentDir,"\\vnchooks.dll");
		}
	UnSetHooks=NULL;
	SetMouseFilterHook=NULL;
	SetKeyboardFilterHook=NULL;
	SetHooks=NULL;
	hModule = LoadLibrary(szCurrentDir);
	if (hModule)
		{
			UnSetHooks = (UnSetHooksFn) GetProcAddress( hModule, "UnSetHooks" );
			SetMouseFilterHook  = (SetMouseFilterHookFn) GetProcAddress( hModule, "SetMouseFilterHook" );
			SetKeyboardFilterHook  = (SetKeyboardFilterHookFn) GetProcAddress( hModule, "SetKeyboardFilterHook" );
			SetHooks  = (SetHooksFn) GetProcAddress( hModule, "SetHooks" );
		}
	On_Off_hookdll=false;
	g_Desktop_running=true;
	hUser32=LoadLibrary("USER32");
	pbi = (pBlockInput)GetProcAddress( hUser32, "BlockInput");
	Temp_Resolution=false;
	m_OrigpollingSet=false;
	m_Origpolling=false;
	DriverWantedSet=false;

	current_monitor=3;
}

vncDesktop::~vncDesktop()
{
	vnclog.Print(LL_INTINFO, VNCLOG("killing screen server\n"));

	// If we created a thread then here we delete it
	// The thread itself does most of the cleanup
	if(m_thread != NULL)
	{
		// Post a close message to quit our message handler thread
		PostMessage(Window(), WM_QUIT, 0, 0);
		vncDesktopThread *thread=(vncDesktopThread*)m_thread;
		while (g_DesktopThread_running!=false)
		{
			PostMessage(Window(), WM_QUIT, 0, 0);
			Sleep(200);
		}
		// Join with the desktop handler thread
		void *returnval;
		m_thread->join(&returnval);
		m_thread = NULL;
	}
	SetDisableInput(false);
	// Let's call Shutdown just in case something went wrong...
	Shutdown();

	// Modif sf@2002
	m_lWList.clear();
	GridsList::iterator iGrid;
	for (iGrid = m_lGridsList.begin(); iGrid != m_lGridsList.end(); iGrid++)
	{
		if (*iGrid)
		{
			// Since we've replaced this:
			// "typedef std::list<RGBPixelList*> GridsList;"
			// with this:
			// "typedef std::list<void*> GridsList;"
			// we must be carefull to avoid memory leaks...
			((RGBPixelList*)(*iGrid))->clear();
			delete ((RGBPixelList*)(*iGrid));
		}
	}
	m_lGridsList.clear();
	if (hModule)FreeLibrary(hModule);
	if (hUser32) FreeLibrary(hUser32);
	g_Desktop_running=false;
}


// Tell the desktop hooks to grab & update a particular rectangle
void
vncDesktop::QueueRect(const rfb::Rect &rect)
{
	ULONG vwParam = MAKELONG(rect.tl.x, rect.tl.y);
	ULONG vlParam = MAKELONG(rect.br.x, rect.br.y);

	PostMessage(Window(), RFB_SCREEN_UPDATE, vwParam, vlParam);
}
	
// Kick the desktop hooks to perform an update
void
vncDesktop::TriggerUpdate()
{
	// Note that we should really lock the update lock here,
	// but there are periodic timer updates anyway, so
	// we don't actually need to.  Something to think about.
	if (!m_update_triggered) {
		m_update_triggered = TRUE;
		g_update_triggered = TRUE;
		if (m_timerid != NULL)
		KillTimer(NULL, m_timerid);
		m_timerid = NULL;
		PostMessage(Window(), WM_TIMER, 0, 0);
	}
}

// Routine to startup and install all the hooks and stuff
BOOL
vncDesktop::Startup()
{
	// Initialise the Desktop object

	// ***
	// vncService::SelectInputWinStation()

	if (!InitDesktop())
		{
			vnclog.Print(LL_INTINFO, VNCLOG("InitDesktop Failed\n"));
			return FALSE;
		}

	// Modif rdv@2002 - v1.1.x - videodriver
	if (!Temp_Resolution)
		{
			vnclog.Print(LL_INTINFO, VNCLOG("InitVideo driver Called no Temp_Resolution\n"));
			if (DriverWantedSet==true)
			{
			m_server->Driver(DriverWanted);
			m_server->Hook(HookWanted);
			DriverWantedSet=false;
			}
			if (m_server->Driver())
				{
					vnclog.Print(LL_INTINFO, VNCLOG("Driver option enabled \n"));
					if(OSVersion()==1 )
						{
#ifdef DRIVER
							InitVideoDriver();
#endif
						}
				}
			vnclog.Print(LL_INTINFO, VNCLOG("Driver option dsiabled \n"));
			if (m_Origpolling) m_server->PollFullScreen(m_Origpolling);
			m_OrigpollingSet=false;
		}
	else
		{
			m_Origpolling=m_server->PollFullScreen();
			m_OrigpollingSet=true;
			m_server->PollFullScreen(TRUE);
			vnclog.Print(LL_INTINFO, VNCLOG("InitVideo driver Called Temp_Resolution\n"));
		}
	
	if (VideoBuffer())
	{
		vnclog.Print(LL_INTINFO, VNCLOG("Break log\n"));
	}
	if (!InitBitmap())
		{
			vnclog.Print(LL_INTINFO, VNCLOG("InitBitmap Failed\n"));
			return FALSE;
		}

	if (!ThunkBitmapInfo())
		{
			vnclog.Print(LL_INTINFO, VNCLOG("ThunkBitmapInfo Failed\n"));
			return FALSE;
		}

	//if (m_server->Driver()) 
	EnableOptimisedBlits();

	if (!SetPixFormat())
		{
		vnclog.Print(LL_INTINFO, VNCLOG("SetPixFormat Failed\n"));
		return FALSE;
		}

	if (!SetPixShifts())
		{
		vnclog.Print(LL_INTINFO, VNCLOG("SetPixShift Failed\n"));
		return FALSE;
		}
	
	if (!SetPalette())
		{
		vnclog.Print(LL_INTINFO, VNCLOG("SetPalette Failed\n"));
		return FALSE;
		}
	
	if (!InitWindow())
		{
		vnclog.Print(LL_INTINFO, VNCLOG("InitWindow failed\n"));
		return FALSE;
		}
#ifdef DRIVER
	if (VideoBuffer())
	{
		pchanges_buf=NULL;
		GETCHANGESBUF *pcommbuffer=m_videodriver->CreateCommunicationBuffer(m_bminfo.bmi.bmiHeader.biSizeImage);
		// we need to check again, communication service can be down
		// In that case driver can not be used
		if (VideoBuffer())
		{
			vnclog.Print(LL_INTINFO, VNCLOG("Removing real Dib buffer and replace by driver communication buffer\n"));
			if (m_membitmap != NULL)
				{
					DeleteObject(m_membitmap);
					m_membitmap = NULL;
				}
			m_DIBbits=pcommbuffer->UserbufferBegin;
			pchanges_buf=pcommbuffer->buffer;
			InvalidateRect(NULL,NULL,TRUE);
		}
	}
#endif

	// Start a timer to handle Polling Mode.  The timer will cause
	// an "idle" event once every 1/10 second, which is necessary if Polling
	// Mode is being used, to cause TriggerUpdate to be called.
	
	m_timerid = SetTimer(m_hwnd, 1, 100, NULL);

	// Initialise the buffer object
	m_buffer.SetDesktop(this);
	GetQuarterSize();

	// Everything is ok, so return TRUE
	return TRUE;
}

// Routine to shutdown all the hooks and stuff
BOOL
vncDesktop::Shutdown()
{
	// If we created a timer then kill it
	if (m_timerid != NULL)
		KillTimer(NULL, m_timerid);

	// If we created a window then kill it and the hooks
	if(m_hwnd != NULL)
	{	
		// Remove the system hooks
		if (UnSetHooks) UnSetHooks(GetCurrentThreadId());

		// The window is being closed - remove it from the viewer list
		ChangeClipboardChain(m_hwnd, m_hnextviewer);

		// Close the hook window
		DestroyWindow(m_hwnd);
		m_hwnd = NULL;
		m_hnextviewer = NULL;
	}

	// Now free all the bitmap stuff
	if (m_hrootdc != NULL)
	{
		// Release our device context
		// if m_hrootdc was created with createdc, we need to use deletedc to release 
//		if (VideoBuffer())
		{
			if (!DeleteDC(m_hrootdc))
				vnclog.Print(LL_INTERR, VNCLOG("failed to DeleteDC hrootdc\n"));
		}
//		else
//		{
//			if(ReleaseDC(NULL, m_hrootdc) == 0)
//				{
//					vnclog.Print(LL_INTERR, VNCLOG("failed to ReleaseDC\n"));
//				}
//		}
		m_hrootdc = NULL;
	}
	if (m_hmemdc != NULL)
	{
		// Release our device context
		if (!DeleteDC(m_hmemdc))
		{
			vnclog.Print(LL_INTERR, VNCLOG("failed to DeleteDC hmemdc\n"));
		}
		m_hmemdc = NULL;
	}
	if (m_membitmap != NULL)
	{
		// Release the custom bitmap, if any
		if (!DeleteObject(m_membitmap))
		{
			vnclog.Print(LL_INTERR, VNCLOG("failed to DeleteObject\n"));
		}
		m_membitmap = NULL;
	}
#ifdef DRIVER
	// Modif rdv@2002 - v1.1.x - videodriver
	ShutdownVideoDriver();
#endif


	return TRUE;
}

// Routine to ensure we're on the correct NT desktop

BOOL
vncDesktop::InitDesktop()
{
	if (vncService::InputDesktopSelected())
		return TRUE;

	// Ask for the current input desktop
	return vncService::SelectDesktop(NULL);
}

// Routine used to close the screen saver, if it's active...

BOOL CALLBACK
KillScreenSaverFunc(HWND hwnd, LPARAM lParam)
{
	char buffer[256];

	// - ONLY try to close Screen-saver windows!!!
	if ((GetClassName(hwnd, buffer, 256) != 0) &&
		(strcmp(buffer, "WindowsScreenSaverClass") == 0))
		PostMessage(hwnd, WM_CLOSE, 0, 0);
	return TRUE;
}

void
vncDesktop::KillScreenSaver()
{
	OSVERSIONINFO osversioninfo;
	osversioninfo.dwOSVersionInfoSize = sizeof(osversioninfo);

	// Get the current OS version
	if (!GetVersionEx(&osversioninfo))
		return;

	vnclog.Print(LL_INTINFO, VNCLOG("KillScreenSaver...\n"));

	// How to kill the screen saver depends on the OS
	switch (osversioninfo.dwPlatformId)
	{
	case VER_PLATFORM_WIN32_WINDOWS:
		{
			// Windows 95

			// Fidn the ScreenSaverClass window
			HWND hsswnd = FindWindow ("WindowsScreenSaverClass", NULL);
			if (hsswnd != NULL)
				PostMessage(hsswnd, WM_CLOSE, 0, 0); 
			break;
		} 
	case VER_PLATFORM_WIN32_NT:
		{
			// Windows NT

			// Find the screensaver desktop
			HDESK hDesk = OpenDesktop(
				"Screen-saver",
				0,
				FALSE,
				DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS
				);
			if (hDesk != NULL)
			{
				vnclog.Print(LL_INTINFO, VNCLOG("Killing ScreenSaver\n"));

				// Close all windows on the screen saver desktop
				EnumDesktopWindows(hDesk, (WNDENUMPROC) &KillScreenSaverFunc, 0);
				CloseDesktop(hDesk);
				// Pause long enough for the screen-saver to close
				//Sleep(2000);
				// Reset the screen saver so it can run again
				SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, TRUE, 0, SPIF_SENDWININICHANGE); 
			}
			break;
		}
	}
}


//
// Modif sf@2002 - Single Window
//
BOOL CALLBACK EnumWindowsHnd(HWND hwnd, LPARAM arg)
{
 int  nRet;
 char buffer[128];
 char szNameAppli[16];

 strcpy(szNameAppli, (LPSTR)(((vncDesktop*)arg)->GetServerPointer()->GetWindowName()));
 szNameAppli[15]=0;
 nRet = GetWindowText(hwnd, buffer, sizeof(buffer));
 if (nRet > 0)
 {
  if ( !strnicmp(buffer, szNameAppli, lstrlen(szNameAppli)))
  {
       ((vncDesktop*)arg)->m_Single_hWnd = hwnd;
       return FALSE;
  }
  else
	  return TRUE;
 }
 return TRUE;
}


BOOL
vncDesktop::InitBitmap()
{	
	// Get the device context for the whole screen and find it's size
	DriverType=NONE;
	if (OSVersion()==1) //XP W2k
		{	
#ifdef DRIVER
			if (VideoBuffer())
				{
					pEnumDisplayDevices pd;
					LPSTR driverName = "Winvnc video hook driver";
					BOOL DriverFound;
					DEVMODE devmode;
					FillMemory(&devmode, sizeof(DEVMODE), 0);
					devmode.dmSize = sizeof(DEVMODE);
					devmode.dmDriverExtra = 0;
					BOOL change = EnumDisplaySettings(NULL,ENUM_CURRENT_SETTINGS,&devmode);
					devmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
					HMODULE hUser32=LoadLibrary("USER32");
					pd = (pEnumDisplayDevices)GetProcAddress( hUser32, "EnumDisplayDevicesA");
						if (pd)
							{
								LPSTR deviceName=NULL;
								DISPLAY_DEVICE dd;
								ZeroMemory(&dd, sizeof(dd));
								dd.cb = sizeof(dd);
								devmode.dmDeviceName[0] = '\0';
								INT devNum = 0;
								BOOL result;
								DriverFound=false;
								while (result = (*pd)(NULL,devNum, &dd,0))
									{
										if (strcmp((const char *)&dd.DeviceString[0], driverName) == 0)
											{
												DriverFound=true;
												break;
											}
										devNum++;
									}
								if (DriverFound)
									{
										deviceName = (LPSTR)&dd.DeviceName[0];
										m_hrootdc = CreateDC("DISPLAY",deviceName,NULL,NULL);
										BOOL change = EnumDisplaySettings(deviceName,ENUM_CURRENT_SETTINGS,&devmode);
										m_ScreenOffsetx=devmode.dmPosition.x;
										m_ScreenOffsety=devmode.dmPosition.y;
										if (m_hrootdc) DriverType=MIRROR;
										Checkmonitors();
										asked_display=m_buffer.GetDisplay();
										current_monitor=1;
										if (asked_display==2 && nr_monitors>1) current_monitor=2;
										if (asked_display==3 && nr_monitors>1) current_monitor=3;
									}
							}
					if (hUser32) FreeLibrary(hUser32);
				}//VIDEOBUFFER
#endif
			}//OS

	// RDV SINGLE WINDOW
	if (m_server->SingleWindow() && m_Single_hWnd==NULL)
	{		
		EnumWindows((WNDENUMPROC)EnumWindowsHnd, (LPARAM) this);
	}

	if (m_hrootdc == NULL) {
		vnclog.Print(LL_INTERR, VNCLOG("No driver used \n"));
		//Multi-Monitor changes
		Checkmonitors();
		asked_display=m_buffer.GetDisplay();
		current_monitor=1;
		if (asked_display==2 && nr_monitors>1) current_monitor=2;
		if (asked_display==3 && nr_monitors>1) current_monitor=3;
		if (current_monitor==3) current_monitor=1;

		if (current_monitor==1)
		{
			m_hrootdc = CreateDC(("DISPLAY"),mymonitor[0].device,NULL,NULL);
			m_ScreenOffsetx=mymonitor[0].offsetx;
			m_ScreenOffsety=mymonitor[0].offsety;
		}
		if (current_monitor==2)
		{
			m_hrootdc =CreateDC(("DISPLAY"),mymonitor[1].device,NULL,NULL);
			m_ScreenOffsetx=mymonitor[1].offsetx;
			m_ScreenOffsety=mymonitor[1].offsety;
		}
		if (current_monitor==3)
		{
			m_hrootdc = GetDC(NULL);
			m_ScreenOffsetx=mymonitor[2].offsetx;
			m_ScreenOffsety=mymonitor[2].offsety;;
		}
		if (m_hrootdc == NULL) {
				vnclog.Print(LL_INTERR, VNCLOG("Failed m_rootdc \n"));
				return FALSE;
		}
		
	}

	m_bmrect = rfb::Rect(0, 0,GetDeviceCaps(m_hrootdc, HORZRES),GetDeviceCaps(m_hrootdc, VERTRES));
	vnclog.Print(LL_INTINFO, VNCLOG("bitmap dimensions are %d x %d\n"), m_bmrect.br.x, m_bmrect.br.y);

	// Create a compatible memory DC
	m_hmemdc = CreateCompatibleDC(m_hrootdc);
	if (m_hmemdc == NULL) {
		vnclog.Print(LL_INTERR, VNCLOG("failed to create compatibleDC(%d)\n"), GetLastError());
		return FALSE;
	}

	// Check that the device capabilities are ok
	if ((GetDeviceCaps(m_hrootdc, RASTERCAPS) & RC_BITBLT) == 0)
	{
		MessageBox(
			NULL,
			"vncDesktop : root device doesn't support BitBlt\n"
			"WinVNC cannot be used with this graphic device driver",
			szAppName,
			MB_ICONSTOP | MB_OK
			);
		return FALSE;
	}
	if ((GetDeviceCaps(m_hmemdc, RASTERCAPS) & RC_DI_BITMAP) == 0)
	{
		MessageBox(
			NULL,
			"vncDesktop : memory device doesn't support GetDIBits\n"
			"WinVNC cannot be used with this graphics device driver",
			szAppName,
			MB_ICONSTOP | MB_OK
			);
		return FALSE;
	}

	// Create the bitmap to be compatible with the ROOT DC!!!
	m_membitmap = CreateCompatibleBitmap(m_hrootdc, m_bmrect.br.x, m_bmrect.br.y);
	if (m_membitmap == NULL) {
		vnclog.Print(LL_INTERR, VNCLOG("failed to create memory bitmap(%d)\n"), GetLastError());
		return FALSE;
	}
	vnclog.Print(LL_INTINFO, VNCLOG("created memory bitmap\n"));

	// Get the bitmap's format and colour details
	int result;
	memset(&m_bminfo, 0, sizeof(m_bminfo));
	m_bminfo.bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	m_bminfo.bmi.bmiHeader.biBitCount = 0;
	result = ::GetDIBits(m_hmemdc, m_membitmap, 0, 1, NULL, &m_bminfo.bmi, DIB_RGB_COLORS);
	if (result == 0) {
		vnclog.Print(LL_INTERR, VNCLOG("unable to get display format\n"));
		return FALSE;
	}
	result = ::GetDIBits(m_hmemdc, m_membitmap,  0, 1, NULL, &m_bminfo.bmi, DIB_RGB_COLORS);
	if (result == 0) {
		vnclog.Print(LL_INTERR, VNCLOG("unable to get display colour info\n"));
		return FALSE;
	}
	vnclog.Print(LL_INTINFO, VNCLOG("got bitmap format\n"));

	// Henceforth we want to use a top-down scanning representation
	m_bminfo.bmi.bmiHeader.biHeight = - abs(m_bminfo.bmi.bmiHeader.biHeight);

	// Is the bitmap palette-based or truecolour?
	m_bminfo.truecolour = (GetDeviceCaps(m_hmemdc, RASTERCAPS) & RC_PALETTE) == 0;
//	InvalidateRect(NULL,NULL,TRUE);
	return TRUE;
}

BOOL
vncDesktop::ThunkBitmapInfo()
{
	// If we leave the pixel format intact, the blist can be optimised (Will Dean's patch)
	m_formatmunged = FALSE;

	// HACK ***.  Optimised blits don't work with palette-based displays, yet
	if (!m_bminfo.truecolour) {
		m_formatmunged = TRUE;
	}

	// Attempt to force the actual format into one we can handle
	// We can handle 8-bit-palette and 16/32-bit-truecolour modes
	switch (m_bminfo.bmi.bmiHeader.biBitCount)
	{
	case 1:
	case 4:
		vnclog.Print(LL_INTINFO, VNCLOG("DBG:used/bits/planes/comp/size = %d/%d/%d/%d/%d\n"),
			(int)m_bminfo.bmi.bmiHeader.biClrUsed,
			(int)m_bminfo.bmi.bmiHeader.biBitCount,
			(int)m_bminfo.bmi.bmiHeader.biPlanes,
			(int)m_bminfo.bmi.bmiHeader.biCompression,
			(int)m_bminfo.bmi.bmiHeader.biSizeImage);
		
		// Correct the BITMAPINFO header to the format we actually want
		m_bminfo.bmi.bmiHeader.biClrUsed = 0;
		m_bminfo.bmi.bmiHeader.biPlanes = 1;
		m_bminfo.bmi.bmiHeader.biCompression = BI_RGB;
		m_bminfo.bmi.bmiHeader.biBitCount = 8;
		m_bminfo.bmi.bmiHeader.biSizeImage =
			abs((m_bminfo.bmi.bmiHeader.biWidth *
				m_bminfo.bmi.bmiHeader.biHeight *
				m_bminfo.bmi.bmiHeader.biBitCount)/ 8);
		m_bminfo.bmi.bmiHeader.biClrImportant = 0;
		m_bminfo.truecolour = FALSE;

		// Display format is non-VNC compatible - use the slow blit method
		m_formatmunged = TRUE;
		break;	
	case 24:
		// Update the bitmapinfo header
		m_bminfo.bmi.bmiHeader.biBitCount = 32;
		m_bminfo.bmi.bmiHeader.biPlanes = 1;
		m_bminfo.bmi.bmiHeader.biCompression = BI_RGB;
		m_bminfo.bmi.bmiHeader.biSizeImage =
			abs((m_bminfo.bmi.bmiHeader.biWidth *
				m_bminfo.bmi.bmiHeader.biHeight *
				m_bminfo.bmi.bmiHeader.biBitCount)/ 8);
		// Display format is non-VNC compatible - use the slow blit method
		m_formatmunged = TRUE;
		break;
	}

	return TRUE;
}

BOOL
vncDesktop::SetPixFormat()
{
 // If we are using a memory bitmap then check how many planes it uses
  // The VNC code can only handle formats with a single plane (CHUNKY pixels)
  if (!m_DIBbits) {
	  vnclog.Print(LL_INTINFO, VNCLOG("DBG:display context has %d planes!\n"),
      GetDeviceCaps(m_hrootdc, PLANES));
	  vnclog.Print(LL_INTINFO, VNCLOG("DBG:memory context has %d planes!\n"),
      GetDeviceCaps(m_hmemdc, PLANES));
	  if (GetDeviceCaps(m_hmemdc, PLANES) != 1)
	  {
		  MessageBox(
			  NULL,
			  "vncDesktop : current display is PLANAR, not CHUNKY!\n"
			  "WinVNC cannot be used with this graphics device driver",
			  szAppName,
			  MB_ICONSTOP | MB_OK
			  );
		  return FALSE;
	  }
  }
	
	
	// Examine the bitmapinfo structure to obtain the current pixel format
	m_scrinfo.format.trueColour = m_bminfo.truecolour;
	m_scrinfo.format.bigEndian = 0;

	// Set up the native buffer width, height and format
	m_scrinfo.framebufferWidth = (CARD16) (m_bmrect.br.x - m_bmrect.tl.x);		// Swap endian before actually sending
	m_scrinfo.framebufferHeight = (CARD16) (m_bmrect.br.y - m_bmrect.tl.y);	// Swap endian before actually sending
	m_scrinfo.format.bitsPerPixel = (CARD8) m_bminfo.bmi.bmiHeader.biBitCount;
	m_scrinfo.format.depth        = (CARD8) m_bminfo.bmi.bmiHeader.biBitCount;

	// Calculate the number of bytes per row
	m_bytesPerRow = m_scrinfo.framebufferWidth * m_scrinfo.format.bitsPerPixel / 8;

	return TRUE;
}

BOOL
vncDesktop::SetPixShifts()
{
	// Sort out the colour shifts, etc.
	DWORD redMask=0, blueMask=0, greenMask = 0;

	switch (m_bminfo.bmi.bmiHeader.biBitCount)
	{
	case 16:
		// Standard 16-bit display
		if (m_bminfo.bmi.bmiHeader.biCompression == BI_RGB)
		{
			// each word single pixel 5-5-5
			redMask = 0x7c00; greenMask = 0x03e0; blueMask = 0x001f;
		}
		else
		{
			if (m_bminfo.bmi.bmiHeader.biCompression == BI_BITFIELDS)
			{
				redMask =   *(DWORD *) &m_bminfo.bmi.bmiColors[0];
				greenMask = *(DWORD *) &m_bminfo.bmi.bmiColors[1];
				blueMask =  *(DWORD *) &m_bminfo.bmi.bmiColors[2];
			}
		}
		break;

	case 32:
		// Standard 24/32 bit displays
		if (m_bminfo.bmi.bmiHeader.biCompression == BI_RGB)
		{
			redMask = 0xff0000; greenMask = 0xff00; blueMask = 0x00ff;
		}
		else
		{
			if (m_bminfo.bmi.bmiHeader.biCompression == BI_BITFIELDS)
			{
				redMask =   *(DWORD *) &m_bminfo.bmi.bmiColors[0];
				greenMask = *(DWORD *) &m_bminfo.bmi.bmiColors[1];
				blueMask =  *(DWORD *) &m_bminfo.bmi.bmiColors[2];
			}
		}
		break;

	default:
		// Other pixel formats are only valid if they're palette-based
		if (m_bminfo.truecolour)
		{
			vnclog.Print(LL_INTERR, "unsupported truecolour pixel format for setpixshifts\n");
			return FALSE;
		}
		return TRUE;
	}

	// Convert the data we just retrieved
	MaskToMaxAndShift(redMask, m_scrinfo.format.redMax, m_scrinfo.format.redShift);
	MaskToMaxAndShift(greenMask, m_scrinfo.format.greenMax, m_scrinfo.format.greenShift);
	MaskToMaxAndShift(blueMask, m_scrinfo.format.blueMax, m_scrinfo.format.blueShift);

	return TRUE;
}

BOOL
vncDesktop::SetPalette()
{
	// Lock the current display palette into the memory DC we're holding
	// *** CHECK THIS FOR LEAKS!
	if (!m_bminfo.truecolour)
	{
		if (!m_DIBbits)
		{
			// - Handle the palette for a non DIB-Section
			
			LOGPALETTE *palette;
			UINT size = sizeof(LOGPALETTE) + (sizeof(PALETTEENTRY) * 256);
			
			palette = (LOGPALETTE *) new char[size];
			if (palette == NULL) {
				vnclog.Print(LL_INTERR, VNCLOG("unable to allocate logical palette\n"));
				return FALSE;
			}
			
			// Initialise the structure
			palette->palVersion = 0x300;
			palette->palNumEntries = 256;
			
			// Get the system colours
			if (GetSystemPaletteEntries(m_hrootdc,
				0, 256, palette->palPalEntry) == 0)
			{
				vnclog.Print(LL_INTERR, VNCLOG("unable to get system palette entries\n"));
				delete [] palette;
				return FALSE;
			}
			
			// Create a palette from those
			HPALETTE pal = CreatePalette(palette);
			if (pal == NULL)
			{
				vnclog.Print(LL_INTERR, VNCLOG("unable to create HPALETTE\n"));
				delete [] palette;
				return FALSE;
			}
			
			// Select the palette into our memory DC
			HPALETTE oldpalette = SelectPalette(m_hmemdc, pal, FALSE);
			if (oldpalette == NULL)
			{
				vnclog.Print(LL_INTERR, VNCLOG("unable to select() HPALETTE\n"));
				delete [] palette;
				DeleteObject(pal);
				return FALSE;
			}
			
			// Worked, so realise the palette
			if (RealizePalette(m_hmemdc) == GDI_ERROR)
				vnclog.Print(LL_INTWARN, VNCLOG("warning - failed to RealizePalette\n"));
			
			// It worked!
			delete [] palette;
			DeleteObject(oldpalette);
			
			vnclog.Print(LL_INTINFO, VNCLOG("initialised palette OK\n"));
			return TRUE;
		}
		else
		{
			// - Handle a DIB-Section's palette
			
			// - Fetch the system palette for the framebuffer
			PALETTEENTRY syspalette[256];
			UINT entries = ::GetSystemPaletteEntries(m_hrootdc, 0, 256, syspalette);
			vnclog.Print(LL_INTERR, VNCLOG("framebuffer has %u palette entries"), entries);
			
			// - Store it and convert it to RGBQUAD format
			RGBQUAD dibpalette[256];
			unsigned int i;
			for (i=0;i<entries;i++) {
				dibpalette[i].rgbRed = syspalette[i].peRed;
				dibpalette[i].rgbGreen = syspalette[i].peGreen;
				dibpalette[i].rgbBlue = syspalette[i].peBlue;
				dibpalette[i].rgbReserved = 0;
			}
			
			// - Set the rest of the palette to something nasty but usable
			for (i=entries;i<256;i++) {
				dibpalette[i].rgbRed = i % 2 ? 255 : 0;
				dibpalette[i].rgbGreen = i/2 % 2 ? 255 : 0;
				dibpalette[i].rgbBlue = i/4 % 2 ? 255 : 0;
				dibpalette[i].rgbReserved = 0;
			}
			
			// - Update the DIB section to use the same palette
			HDC bitmapDC=::CreateCompatibleDC(m_hrootdc);
			if (!bitmapDC) {
				vnclog.Print(LL_INTERR, VNCLOG("unable to create temporary DC"), GetLastError());
				return FALSE;
			}
			HBITMAP old_bitmap = (HBITMAP)::SelectObject(bitmapDC, m_membitmap);
			if (!old_bitmap) {
				vnclog.Print(LL_INTERR, VNCLOG("unable to select DIB section into temporary DC"), GetLastError());
				return FALSE;
			}
			UINT entries_set = ::SetDIBColorTable(bitmapDC, 0, 256, dibpalette);
			if (entries_set == 0) {
				vnclog.Print(LL_INTERR, VNCLOG("unable to set DIB section palette"), GetLastError());
				return FALSE;
			}
			if (!::SelectObject(bitmapDC, old_bitmap)) {
				vnclog.Print(LL_INTERR, VNCLOG("unable to restore temporary DC bitmap"), GetLastError());
				return FALSE;
			}
		}
    }
 
	// Not a palette based local screen - forget it!
	vnclog.Print(LL_INTERR, VNCLOG("no palette data for truecolour display\n"));
	return TRUE;
}
////////////////////////////////////////////////////////////////////////////////
DWORD WINAPI Driverwatch(LPVOID lpParam)
{
	//Mouse shape changed
	if(OSVersion()==1)
	{
		HANDLE event;
		//DrvWatch *mywatch=(DrvWatch*)lpParam;
		HWND hwnd=(HWND)lpParam;
		event=NULL;
		while (event==NULL)
		{
			event = OpenEvent (SYNCHRONIZE, FALSE, "VncEvent") ;
			Sleep(900);
			if (!IsWindow(hwnd)) 
			{
				if (event) CloseHandle(event);
				return 0;
			}
		}
		SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
		for (;;)
		{
		if (WaitForSingleObject(event, 2000) == WAIT_OBJECT_0)
			{
				PostMessage(hwnd, WM_USER, 0, 0);
			}
		if (!IsWindow(hwnd) || !g_Desktop_running)
		{
			if (event) CloseHandle(event);
			break;
		}
		}
	}
	return 0;
}
////////////////////////////////////////////////////////////////////////////////
DWORD WINAPI Driverwatch2(LPVOID lpParam)
{
	//new screen update
	if(OSVersion()==1)
	{
		HANDLE event;
		//DrvWatch *mywatch=(DrvWatch*)lpParam;
		HWND hwnd=(HWND)lpParam;
		event=NULL;
		while (event==NULL)
		{
			event = OpenEvent (SYNCHRONIZE, FALSE, "VncEvent2") ;
			Sleep(900);
			if (!IsWindow(hwnd)) 
			{
				if (event) CloseHandle(event);
				return 0;
			}
		}
		SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
		counterwatch=0;
		for (;;)
		{
		if (WaitForSingleObject(event, 50) == WAIT_OBJECT_0)
			{
				//if (!g_update_triggered)
				PostMessage(hwnd, WM_TIMER, 0, 0);
				Sleep(100);
			}
		else
			{
				counterwatch++;
				if (counterwatch==100)
					{
						PostMessage(hwnd, WM_USER+2, 0, 0);
						counterwatch=0;
				}
			}
		if (!IsWindow(hwnd) || !g_Desktop_running) 
			{
				if (event) CloseHandle(event);
				break;
			}
		}
	}
	return 0;
}
////////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK DesktopWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam);

ATOM m_wndClass = 0;

BOOL
vncDesktop::InitWindow()
{
	if (m_wndClass == 0) {
		// Create the window class
		WNDCLASSEX wndclass;

		wndclass.cbSize			= sizeof(wndclass);
		wndclass.style			= 0;
		wndclass.lpfnWndProc	= &DesktopWndProc;
		wndclass.cbClsExtra		= 0;
		wndclass.cbWndExtra		= 0;
		wndclass.hInstance		= hAppInstance;
		wndclass.hIcon			= NULL;
		wndclass.hCursor		= NULL;
		wndclass.hbrBackground	= (HBRUSH) GetStockObject(WHITE_BRUSH);
		wndclass.lpszMenuName	= (const char *) NULL;
		wndclass.lpszClassName	= szDesktopSink;
		wndclass.hIconSm		= NULL;

		// Register it
		m_wndClass = RegisterClassEx(&wndclass);
		if (!m_wndClass) {
			vnclog.Print(LL_INTERR, VNCLOG("failed to register window class\n"));
			return FALSE;
		}
	}

	// And create a window
	m_hwnd = CreateWindow(szDesktopSink,
				"WinVNC",
				WS_OVERLAPPEDWINDOW,
				CW_USEDEFAULT,
				CW_USEDEFAULT,
				400, 200,
				NULL,
				NULL,
				hAppInstance,
				NULL);

	if (m_hwnd == NULL) {
		vnclog.Print(LL_INTERR, VNCLOG("failed to create hook window\n"));
		return FALSE;
	}

	// Set the "this" pointer for the window
	SetWindowLong(m_hwnd, GWL_USERDATA, (long)this);

	// Enable clipboard hooking
	m_hnextviewer = SetClipboardViewer(m_hwnd);
	StopDriverWatches=false;
		DrvWatch mywatch;
		mywatch.stop=&StopDriverWatches;
		mywatch.hwnd=m_hwnd;
	if (VideoBuffer())
	{
		DWORD myword;
		HANDLE T1=CreateThread(NULL,0,Driverwatch,m_hwnd,0,&myword);
//		HANDLE T2=CreateThread(NULL,0,Driverwatch2,m_hwnd,0,&myword);
//		CloseHandle(T2);
		CloseHandle(T1);
	}

	return TRUE;
}

void
vncDesktop::EnableOptimisedBlits()
{
	vnclog.Print(LL_INTINFO, VNCLOG("attempting to enable DIBsection blits\n"));

	// Create a new DIB section
	//HBITMAP tempbitmap=NULL;
	HBITMAP tempbitmap = CreateDIBSection(m_hmemdc, &m_bminfo.bmi, DIB_RGB_COLORS, &m_DIBbits, NULL, 0);
	if (tempbitmap == NULL) {
		vnclog.Print(LL_INTINFO, VNCLOG("failed to build DIB section - reverting to slow blits\n"));
		m_DIBbits = NULL;
		return;
	}

	// Delete the old memory bitmap
	if (m_membitmap != NULL) {
		DeleteObject(m_membitmap);
		m_membitmap = NULL;
	}

	// Replace old membitmap with DIB section
	m_membitmap = tempbitmap;
	vnclog.Print(LL_INTINFO, VNCLOG("enabled fast DIBsection blits OK\n"));
}

BOOL
vncDesktop::Init(vncServer *server)
{
	vnclog.Print(LL_INTINFO, VNCLOG("initialising desktop handler\n"));

	// Save the server pointer
	m_server = server;

	// sf@2005
	if (OSVersion()==1)
	m_fCaptureAlphaBlending = m_server->CaptureAlphaBlending();
	else m_fCaptureAlphaBlending=false;

	// Load in the arrow cursor
	m_hdefcursor = LoadCursor(NULL, IDC_ARROW);
	m_hcursor = m_hdefcursor;
	m_hOldcursor = m_hdefcursor; //sf@2002

	// Spawn a thread to handle that window's message queue
	vncDesktopThread *thread = new vncDesktopThread;
	if (thread == NULL) {
		vnclog.Print(LL_INTERR, VNCLOG("failed to start hook thread\n"));
		return FALSE;
	}
	m_thread = thread;
	//SINGEL WINDOW
	SWinit();
	// InitHookSettings();
	return thread->Init(this, m_server);
}

int
vncDesktop::ScreenBuffSize()
{
	return m_scrinfo.format.bitsPerPixel/8 *
		m_scrinfo.framebufferWidth *
		m_scrinfo.framebufferHeight;
}

void
vncDesktop::FillDisplayInfo(rfbServerInitMsg *scrinfo)
{
	memcpy(scrinfo, &m_scrinfo, sz_rfbServerInitMsg);
}
#ifndef CAPTUREBLT
#define CAPTUREBLT  0x40000000
#endif

// Function to capture an area of the screen immediately prior to sending
// an update.
void
vncDesktop::CaptureScreen(const rfb::Rect &rect, BYTE *scrBuff, UINT scrBuffSize)
{
	assert(rect.enclosed_by(m_bmrect));

	// Select the memory bitmap into the memory DC
	HBITMAP oldbitmap;
	if ((oldbitmap = (HBITMAP) SelectObject(m_hmemdc, m_membitmap)) == NULL)
		return;

	// Capture screen into bitmap
	BOOL blitok = BitBlt(m_hmemdc, rect.tl.x, rect.tl.y,
		(rect.br.x-rect.tl.x),
		(rect.br.y-rect.tl.y),
		m_hrootdc, rect.tl.x, rect.tl.y, m_fCaptureAlphaBlending ? (CAPTUREBLT | SRCCOPY) : SRCCOPY);

	// Select the old bitmap back into the memory DC
	SelectObject(m_hmemdc, oldbitmap);

	if (blitok) {
		// Copy the new data to the screen buffer (CopyToBuffer optimises this if possible)
		CopyToBuffer(rect, scrBuff, scrBuffSize);
	}

}

// Add the mouse pointer to the buffer
void
vncDesktop::CaptureMouse(BYTE *scrBuff, UINT scrBuffSize)
{
	POINT CursorPos;
	ICONINFO IconInfo;

	// If the mouse cursor handle is invalid then forget it
	if (m_hcursor == NULL)
		return;

	// Get the cursor position
	if (!GetCursorPos(&CursorPos))
		return;
	//vnclog.Print(LL_INTINFO, VNCLOG("CursorPos %i %i\n"),CursorPos.x, CursorPos.y);
	// Translate position for hotspot
	if (GetIconInfo(m_hcursor, &IconInfo))
	{
		CursorPos.x -= ((int) IconInfo.xHotspot);
		CursorPos.y -= ((int) IconInfo.yHotspot);
		/// Buffer has (0,0) coordinates, Cursor (screencoordinates)
		CursorPos.x -= m_ScreenOffsetx;
		CursorPos.y -= m_ScreenOffsety;
		///
		if (IconInfo.hbmMask != NULL)
			DeleteObject(IconInfo.hbmMask);
		if (IconInfo.hbmColor != NULL)
			DeleteObject(IconInfo.hbmColor);
	}

	// Select the memory bitmap into the memory DC
	HBITMAP oldbitmap;
	if ((oldbitmap = (HBITMAP) SelectObject(m_hmemdc, m_membitmap)) == NULL)
		return;

	// Draw the cursor
	DrawIconEx(
		m_hmemdc,									// handle to device context 
		CursorPos.x, CursorPos.y,
		m_hcursor,									// handle to icon to draw 
		0,0,										// width of the icon 
		0,											// index of frame in animated cursor 
		NULL,										// handle to background brush 
		DI_NORMAL | DI_COMPAT						// icon-drawing flags 
		);

	// Select the old bitmap back into the memory DC
	SelectObject(m_hmemdc, oldbitmap);

	// Save the bounding rectangle
	m_cursorpos.tl = CursorPos;
	m_cursorpos.br = rfb::Point(GetSystemMetrics(SM_CXCURSOR),
		GetSystemMetrics(SM_CYCURSOR)).translate(CursorPos);

	// Clip the bounding rect to the screen
	// Copy the mouse cursor into the screen buffer, if any of it is visible
	m_cursorpos = m_cursorpos.intersect(m_bmrect);
	if (!m_cursorpos.is_empty()) {
		CopyToBuffer(m_cursorpos, scrBuff, scrBuffSize);
	}
}


// CURSOR HANDLING
// Obtain cursor image data in server's local format.
// The length of databuf[] should be at least (width * height * 4).
BOOL
vncDesktop::GetRichCursorData(BYTE *databuf, HCURSOR hcursor, int width, int height)
{
	// Protect the memory bitmap (is it really necessary here?)
	omni_mutex_lock l(m_update_lock);

	// Create bitmap, select it into memory DC
	HBITMAP membitmap = CreateCompatibleBitmap(m_hrootdc, width, height);
	if (membitmap == NULL) {
		return FALSE;
	}
	HBITMAP oldbitmap = (HBITMAP) SelectObject(m_hmemdc, membitmap);
	if (oldbitmap == NULL) {
		DeleteObject(membitmap);
		return FALSE;
	}

	// Draw the cursor
	DrawIconEx(m_hmemdc, 0, 0, hcursor, 0, 0, 0, NULL, DI_IMAGE);
	SelectObject(m_hmemdc, oldbitmap);

	// Prepare BITMAPINFO structure (copy most m_bminfo fields)
	BITMAPINFO *bmi = (BITMAPINFO *)calloc(1, sizeof(BITMAPINFO) + 256 * sizeof(RGBQUAD));
	memcpy(bmi, &m_bminfo.bmi, sizeof(BITMAPINFO) + 256 * sizeof(RGBQUAD));
	bmi->bmiHeader.biWidth = width;
	bmi->bmiHeader.biHeight = -height;

	// Clear data buffer and extract RGB data
	memset(databuf, 0x00, width * height * 4);
	int lines = GetDIBits(m_hmemdc, membitmap, 0, height, databuf, bmi, DIB_RGB_COLORS);

	// Cleanup
	free(bmi);
	DeleteObject(membitmap);

	return (lines != 0);
}

// Return the current mouse pointer position
rfb::Rect
vncDesktop::MouseRect()
{
	return m_cursorpos;
}

void
vncDesktop::SetCursor(HCURSOR cursor)
{
	if (cursor == NULL)
		m_hcursor = m_hdefcursor;
	else
	{
		m_hOldcursor = m_hcursor; // sf@2002
		m_hcursor = cursor;
	}
}

// Manipulation of the clipboard
void vncDesktop::SetClipText(char* rfbStr)
{
  int len = strlen(rfbStr);
  char* winStr = new char[len*2+1];

  int j = 0;
  for (int i = 0; i < len; i++)
  {
    if (rfbStr[i] == 10)
      winStr[j++] = 13;
    winStr[j++] = rfbStr[i];
  }
  winStr[j++] = 0;

  // Open the system clipboard
  if (OpenClipboard(m_hwnd))
  {
    // Empty it
    if (EmptyClipboard())
    {
      HANDLE hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, j);

      if (hMem != NULL)
      {
        LPSTR pMem = (char*)GlobalLock(hMem);

        // Get the data
        strcpy(pMem, winStr);

        // Tell the clipboard
        GlobalUnlock(hMem);
        SetClipboardData(CF_TEXT, hMem);
      }
    }
  }

  delete [] winStr;

  // Now close it
  CloseClipboard();
}

// INTERNAL METHODS

inline void
vncDesktop::MaskToMaxAndShift(DWORD mask, CARD16 &max, CARD8 &shift)
{
	for (shift = 0; (mask & 1) == 0; shift++)
		mask >>= 1;
	max = (CARD16) mask;
}

// Copy data from the memory bitmap into a buffer
void
vncDesktop::CopyToBuffer(const rfb::Rect &rect, BYTE *destbuff, UINT destbuffsize)
{
	// Finish drawing anything in this thread 
	// Wish we could do this for the whole system - maybe we should
	// do something with LockWindowUpdate here.
	GdiFlush();

	// Are we being asked to blit from the DIBsection to itself?
	if (destbuff == m_DIBbits) {
		// Yes.  Ignore the request!
		return;
	}

	int y_inv;
	BYTE * destbuffpos;

	// Calculate the scanline-ordered y position to copy from
	y_inv = m_scrinfo.framebufferHeight-rect.tl.y-(rect.br.y-rect.tl.y);

	// Calculate where in the output buffer to put the data
	destbuffpos = destbuff + (m_bytesPerRow * rect.tl.y);

	// Set the number of bytes for GetDIBits to actually write
	// NOTE : GetDIBits pads the destination buffer if biSizeImage < no. of bytes required
	m_bminfo.bmi.bmiHeader.biSizeImage = (rect.br.y-rect.tl.y) * m_bytesPerRow;

	// Get the actual bits from the bitmap into the bit buffer
	// If fast (DIBsection) blits are disabled then use the old GetDIBits technique
	if (m_DIBbits == NULL) {
		if (GetDIBits(m_hmemdc, m_membitmap, y_inv,
					(rect.br.y-rect.tl.y), destbuffpos,
					&m_bminfo.bmi, DIB_RGB_COLORS) == 0)
		{
#ifdef _MSC_VER
			_RPT1(_CRT_WARN, "vncDesktop : [1] GetDIBits failed! %d\n", GetLastError());
			_RPT3(_CRT_WARN, "vncDesktop : thread = %d, DC = %d, bitmap = %d\n", omni_thread::self(), m_hmemdc, m_membitmap);
			_RPT2(_CRT_WARN, "vncDesktop : y = %d, height = %d\n", y_inv, (rect.br.y-rect.tl.y));
#endif
		}
	} else {
		// Fast blits are enabled.  [I have a sneaking suspicion this will never get used, unless
		// something weird goes wrong in the code.  It's here to keep the function general, though!]

		int bytesPerPixel = m_scrinfo.format.bitsPerPixel / 8;
		BYTE *srcbuffpos = (BYTE*)m_DIBbits;

		srcbuffpos += (m_bytesPerRow * rect.tl.y) + (bytesPerPixel * rect.tl.x);
		destbuffpos += bytesPerPixel * rect.tl.x;

		int widthBytes = (rect.br.x-rect.tl.x) * bytesPerPixel;

		for(int y = rect.tl.y; y < rect.br.y; y++)
		{
			memcpy(destbuffpos, srcbuffpos, widthBytes);
			srcbuffpos += m_bytesPerRow;
			destbuffpos += m_bytesPerRow;
		}
	}
}

// Routine to find out which windows have moved
// If copyrect detection isn't perfect then this call returns
// the copyrect destination region, to allow the caller to check
// for mistakes
bool
vncDesktop::CalcCopyRects(rfb::UpdateTracker &tracker)
{
	HWND foreground = GetForegroundWindow();
	RECT foreground_rect;

	// Actually, we just compare the new and old foreground window & its position
	if (foreground != m_foreground_window) {
		m_foreground_window=foreground;
		// Is the window invisible or can we not get its rect?
		if (!IsWindowVisible(foreground) ||
			!GetWindowRect(foreground, &foreground_rect)) {
			//Buffer coordinates
			m_foreground_window_rect.clear();
		} else {
			foreground_rect.left-=m_ScreenOffsetx;
			foreground_rect.right-=m_ScreenOffsetx;
			foreground_rect.top-=m_ScreenOffsety;
			foreground_rect.bottom-=m_ScreenOffsety;
			m_foreground_window_rect = foreground_rect;
		}
	} else {
		// Same window is in the foreground - let's see if it's moved
		RECT destrect;
		rfb::Rect dest;
		rfb::Point source;

		// Get the window rectangle
		if (IsWindowVisible(foreground) && GetWindowRect(foreground, &destrect))
		{
			//screen to buffer coordinates
			destrect.left-=m_ScreenOffsetx;
			destrect.right-=m_ScreenOffsetx;
			destrect.top-=m_ScreenOffsety;
			destrect.bottom-=m_ScreenOffsety;

			rfb::Rect old_foreground_window_rect = m_foreground_window_rect;
			source = m_foreground_window_rect.tl;
			m_foreground_window_rect = dest = destrect;
			if (!dest.is_empty() && !old_foreground_window_rect.is_empty())
			{
				// Got the destination position.  Now send to clients!
				if (!source.equals(dest.tl))
				{
					rfb::Point delta;
					delta= rfb::Point(dest.tl.x-source.x, dest.tl.y-source.y);
				//	if (dest.tl.x-source.x==0 || dest.tl.y-source.y==0) return false;

					// Clip the destination rectangle
					dest = dest.intersect(m_bmrect);
					if (dest.is_empty()) return false;
					// Clip the source rectangle
					dest = dest.translate(delta.negate()).intersect(m_bmrect);
					m_buffer.ClearCacheRect(dest);
					dest = dest.translate(delta);
					m_buffer.ClearCacheRect(dest);
					if (!dest.is_empty()) {
						tracker.add_copied(dest, delta);
						return true;
					}
				}
			}
		} else {
			m_foreground_window_rect.clear();
		}
	}
	return false;
}

// Window procedure for the Desktop window
LRESULT CALLBACK
DesktopWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
	vncDesktop *_this = (vncDesktop*)GetWindowLong(hwnd, GWL_USERDATA);

	switch (iMsg)
	{
	///ddihook
	case WM_SYSCOMMAND:
		// User has clicked an item on the tray menu
		switch (wParam)
		{
			case SC_MONITORPOWER:
				vnclog.Print(LL_INTINFO, VNCLOG("Monitor2 %i\n"),lParam);
		}
		vnclog.Print(LL_INTINFO, VNCLOG("Monitor3 %i %i\n"),wParam,lParam);
		return DefWindowProc(hwnd, iMsg, wParam, lParam);
	case WM_POWER:
	case WM_POWERBROADCAST:
		// User has clicked an item on the tray menu
		switch (wParam)
		{
			case SC_MONITORPOWER:
				vnclog.Print(LL_INTINFO, VNCLOG("Monitor2 %i\n"),lParam);
		}
		vnclog.Print(LL_INTINFO, VNCLOG("Power3 %i %i\n"),wParam,lParam);
		return DefWindowProc(hwnd, iMsg, wParam, lParam);

	case WM_COPYDATA:
			_this->pMyCDS= (PCOPYDATASTRUCT) lParam;
			if (_this->pMyCDS->dwData==112233)
			{
					DWORD mysize=_this->pMyCDS->cbData;
					char mytext[1024];
					char *myptr;
					char split[4][6];
					strcpy(mytext,(LPCSTR)_this->pMyCDS->lpData);
					myptr=mytext;
					for (int j =0; j<(mysize/20);j++)
					{
						for (int i=0;i<4;i++)
							{
								strcpy(split[i],"     ");
								strncpy(split[i],myptr,4);
								myptr=myptr+5;
							}
						_this->QueueRect(rfb::Rect(atoi(split[0]), atoi(split[1]), atoi(split[2]), atoi(split[3])));
					}
					
			}
			//vnclog.Print(LL_INTINFO, VNCLOG("copydata\n"));	
			return 0;

	case WM_DISPLAYCHANGE:
		// The display resolution is changing
		// We must kick off any clients since their screen size will be wrong
		// WE change the clients screensize, if they support it.
		vnclog.Print(LL_INTERR, VNCLOG("WM_DISPLAYCHANGE\n"));
		// We First check if the Resolution changed is caused by a temp resolution switch
		// For a temp resolution we don't use the driver, to fix the mirror driver
		// to the new change, a resolution switch is needed, preventing screensaver locking.
#ifdef DRIVER
		if (_this->m_videodriver != NULL)
			{	
				_this->Temp_Resolution=_this->m_videodriver->Tempres();
				_this->m_videodriver->Temp_Resolution=_this->m_videodriver->Tempres();
			}
		else
#endif
			_this->Temp_Resolution=false;
#ifdef DRIVER
		if (_this->m_videodriver != NULL) //Video driver active
		{
			if (!_this->m_videodriver->blocked)
			{
				_this->m_displaychanged = TRUE;
				if ( _this->Temp_Resolution) 
					{
						_this->m_hookdriver=false;
						vnclog.Print(LL_INTERR, VNCLOG("Temp Resolution switch detected\n"));
					}	
				else
					{
						_this->m_hookdriver=true;
						_this->m_videodriver->blocked=true;
						vnclog.Print(LL_INTERR, VNCLOG("Resolution switch detected, driver active\n"));
					}

				
			}
			else
			{
				//Remove display change, cause by driver activation
				_this->m_videodriver->blocked=false;
				vnclog.Print(LL_INTERR, VNCLOG("Resolution switch by driver activation removed\n"));
			}
		}
		else 
#endif
		{
				_this->m_displaychanged = TRUE;
				_this->m_hookdriver=true;
				vnclog.Print(LL_INTERR, VNCLOG("Resolution switch detected, driver NOT active\n"));
			
		}
		return 0;

	case WM_SYSCOLORCHANGE:
	case WM_PALETTECHANGED:
		// The palette colours have changed, so tell the server

		// Get the system palette
		if (!_this->SetPalette())
			PostQuitMessage(0);

		// Update any palette-based clients, too
		_this->m_server->UpdatePalette();
		return 0;

		// CLIPBOARD MESSAGES

	case WM_CHANGECBCHAIN:
		// The clipboard chain has changed - check our nextviewer handle
		if ((HWND)wParam == _this->m_hnextviewer)
			_this->m_hnextviewer = (HWND)lParam;
		else
			if (_this->m_hnextviewer != NULL)
				SendMessage(_this->m_hnextviewer,
							WM_CHANGECBCHAIN,
							wParam, lParam);

		return 0;

	case WM_DRAWCLIPBOARD:
		// The clipboard contents have changed
		if((GetClipboardOwner() != _this->Window()) &&
		    _this->m_initialClipBoardSeen &&
			_this->m_clipboard_active)
		{
			LPSTR cliptext = NULL;

			// Open the clipboard
			if (OpenClipboard(_this->Window()))
			{
				// Get the clipboard data
				HGLOBAL cliphandle = GetClipboardData(CF_TEXT);
				if (cliphandle != NULL)
				{
					LPSTR clipdata = (LPSTR) GlobalLock(cliphandle);

					// Copy it into a new buffer
					if (clipdata == NULL)
						cliptext = NULL;
					else
						cliptext = strdup(clipdata);

					// Release the buffer and close the clipboard
					GlobalUnlock(cliphandle);
				}

				CloseClipboard();
			}

			if (cliptext != NULL)
			{
				int cliplen = strlen(cliptext);
				LPSTR unixtext = (char *)malloc(cliplen+1);

				// Replace CR-LF with LF - never send CR-LF on the wire,
				// since Unix won't like it
				int unixpos=0;
				for (int x=0; x<cliplen; x++)
				{
					if (cliptext[x] != '\x0d')
					{
						unixtext[unixpos] = cliptext[x];
						unixpos++;
					}
				}
				unixtext[unixpos] = 0;

				// Free the clip text
				free(cliptext);
				cliptext = NULL;

				// Now send the unix text to the server
				_this->m_server->UpdateClipText(unixtext);

				free(unixtext);
			}
		}

		_this->m_initialClipBoardSeen = TRUE;

		if (_this->m_hnextviewer != NULL)
		{
			// Pass the message to the next window in clipboard viewer chain.  
			return SendMessage(_this->m_hnextviewer, WM_DRAWCLIPBOARD, 0,0); 
		}

		return 0;

	default:
		return DefWindowProc(hwnd, iMsg, wParam, lParam);
	}
	return 0;
}


// Modif rdv@2002 Dis/enable input
void
vncDesktop::SetDisableInput(bool enabled)
{
	typedef DWORD (WINAPI *PSLWA)(HWND, DWORD, BYTE, DWORD);
	PSLWA pSetLayeredWindowAttributes;
	HMODULE hDLL = LoadLibrary ("user32");
	pSetLayeredWindowAttributes = (PSLWA) GetProcAddress(hDLL,"SetLayeredWindowAttributes");
	if (!pSetLayeredWindowAttributes) m_server->BlackAlphaBlending(false);
	if (VideoBuffer())m_server->BlackAlphaBlending(false);

	//BlockInput block everything on non w2k and XP
	//if hookdll is used, he take care of input blocking
	if (OSVersion()==1) 
		{
			if (pbi) (*pbi)(enabled);
		}
	else
		{
			m_server->DisableLocalInputs(enabled);
			On_Off_hookdll=true;
		}
	// Also Turn Off the Monitor if allowed ("Blank Screen", "Blank Monitor")
	if (m_server->BlankMonitorEnabled())
	if (enabled)
	{
		if (!m_server->BlackAlphaBlending())
		{
		SetProcessShutdownParameters(0x100, 0);
		SystemParametersInfo(SPI_GETPOWEROFFTIMEOUT, 0, &OldPowerOffTimeout, 0);
		SystemParametersInfo(SPI_SETPOWEROFFTIMEOUT, 100, NULL, 0);
		SystemParametersInfo(SPI_SETPOWEROFFACTIVE, 1, NULL, 0);
		SendMessage(m_hwnd,WM_SYSCOMMAND,SC_MONITORPOWER,(LPARAM)2);
		}
		else
		{
			HANDLE ThreadHandle2;
			DWORD dwTId;
			ThreadHandle2 = CreateThread(NULL, 0, BlackWindow, NULL, 0, &dwTId);
			CloseHandle(ThreadHandle2);
			OldCaptureBlending=m_fCaptureAlphaBlending;
			m_fCaptureAlphaBlending=false;
		}
	}
	else // Monitor On
	{
		if (!m_server->BlackAlphaBlending())
		{
		if (OldPowerOffTimeout!=0)
			SystemParametersInfo(SPI_SETPOWEROFFTIMEOUT, OldPowerOffTimeout, NULL, 0);
		SystemParametersInfo(SPI_SETPOWEROFFACTIVE, 0, NULL, 0);
		SendMessage(m_hwnd,WM_SYSCOMMAND,SC_MONITORPOWER,(LPARAM)-1);
		OldPowerOffTimeout=0;
		}
		else
		{
			HWND Blackhnd = FindWindow(("blackscreen"), 0);
			if (Blackhnd) PostMessage(Blackhnd, WM_CLOSE, 0, 0);
			m_fCaptureAlphaBlending=OldCaptureBlending;
		}
	}
}


// SW
void vncDesktop::SetSW(int x,int y)
{
	POINT point;
	point.x=x;
	point.y=y;
	vnclog.Print(LL_INTERR, VNCLOG("SETW %i %i\n"),x,y);
	if (x <= 5 && y<=5 && x>-5 && y>-5) 
		{
			switch(asked_display)
			{
				case 1:
				m_buffer.Display(-1);
				m_buffer.Display(2);
				m_Single_hWnd=NULL;
				//m_SWtoDesktop=TRUE;
				break;
				case 2:
				m_buffer.Display(1);
				m_buffer.Display(2);
				m_Single_hWnd=NULL;
				break;
				case 3:
				m_buffer.Display(1);
				m_buffer.Display(-2);
				m_Single_hWnd=NULL;
				break;
			}
			return;
	}

	m_Single_hWnd=WindowFromPoint(point);

	if (m_Single_hWnd==GetDesktopWindow())
		{
			LPGETMONITORINFO GetMonitorInfo=NULL;
			LPMONITOTFROMPOINT MonitorFromPoint=NULL;
			MONITORINFO monitorinfo;
			monitorinfo.cbSize=sizeof(MONITORINFO);
			GetMonitorInfo=(LPGETMONITORINFO) GetProcAddress(LoadLibrary("user32.dll"), "GetMonitorInfoA");
			MonitorFromPoint=(LPMONITOTFROMPOINT) GetProcAddress(LoadLibrary("user32.dll"), "MonitorFromPointA");
			if (GetMonitorInfo && MonitorFromPoint)
			{
				HMONITOR hmonitor=MonitorFromPoint(point,MONITOR_DEFAULTTONEAREST);
				GetMonitorInfo(hmonitor,&monitorinfo);
				if (monitorinfo.dwFlags ==MONITORINFOF_PRIMARY) 
				{
					m_buffer.Display(1);
					m_buffer.Display(-2);
					m_Single_hWnd=NULL;
				}
				else
				{
					m_buffer.Display(-1);
					m_buffer.Display(2);
					m_Single_hWnd=NULL;
				}
			}
			return;
		}

	if (m_Single_hWnd==NULL) m_server->SingleWindow(false);
	else
		{
		HWND parent;
		while((parent=GetParent(m_Single_hWnd))!=NULL)
			{
				m_Single_hWnd=parent;
			}
		m_server->SingleWindow(true);
		}

}

/***************************************************************************
* Ultravnc use 2 different software hooking methods 
* hookdll: Hook the messages between the windows (nt,9.x)
* ddihook: Hook the messages to the video buffer (9.x)
* Driver hooking
* Driver hooking is done at kernel level.
* and exist of 2 parts
* update mechanism: rectangles are send from kernel to vnc (shared mem)
* shared video buffer: direct access the memeory without bitblit
*  m_hookdriver: use driver update mechanism (ddihook in case of 9.x)
*  m_hookdll: use software update mechanism
***************************************************************************/
// Modif rdv@2002 - v1.1.x - videodriver
BOOL vncDesktop::VideoBuffer()
{
	#ifdef DRIVER
	
	//Always access the shared mememory thru this function
	// Check NULL
	if (m_videodriver==NULL) return FALSE;
	if (IsBadReadPtr(m_videodriver,1)) return FALSE;
	// If we reach this place, the driver was active
	if (m_videodriver->driver_succes) return true;

	if (!m_videodriver->driver_succes && Temp_Resolution) return false;
	if (!m_videodriver->driver_succes && !Temp_Resolution)
	{
		m_videodriver->StopMirroring();
		m_hookswitch=true;
		Hookdll_Changed=true;
		return FALSE;
	}
	#endif
	return FALSE;
}

DWORD WINAPI Warningbox_non_locked(LPVOID lpParam)
{
	MessageBox(NULL,"Current driver to old for this version \nUpdate driver or disable Video hook driver\n in the server properties window","",0);
	return 0;
}
#ifdef DRIVER
// Modif rdv@2002 - v1.1.x - videodriver
BOOL vncDesktop::InitVideoDriver()
{
	omni_mutex_lock l(m_videodriver_lock);
	
	if(OSVersion()!=1 ) return true; //we need w2k or xp
	vnclog.Print(LL_INTERR, VNCLOG("Driver option is enabled\n"));
	// If m_videodriver exist, the driver was activated.
	// This does not mean he is still active
	// Screen switching disable the driver at kernel level
	// The pointers to the shared memory still exist, but no more memeory
	// associated...This is the biggest risk when using the driver
	//
/*	if (!RestartDriver)
	{
		m_hookdriver=false;
		m_hookdll=true;
		return false;
	}*/

	// First check driver version
	if (m_videodriver!=NULL) 
	{
		
								vnclog.Print(LL_INTERR, VNCLOG("Closing pending driver driver version\n"));
								if (m_videodriver!=NULL) delete m_videodriver;
			
	}

	m_videodriver=new vncVideoDriver;
	//try to use the mirror driver if he is still active
/*	if (m_videodriver->ExistMirrorDriver())
	{
		char buffer[256];
		if (m_videodriver->GetDllProductVersion("vncdrv.dll",buffer,254))
				{
					DWORD myword;
					if (strcmp(buffer,"1.00.18")<0)
					{
						HANDLE T1=CreateThread(NULL,0,Warningbox_non_locked,m_hwnd,0,&myword);
						CloseHandle(T1);
						vnclog.Print(LL_INTERR, VNCLOG("Wrong driver version\n"));
						if (m_videodriver!=NULL) delete m_videodriver;
						m_videodriver=NULL;
						m_hookdriver=false;
						m_hookdll=true;
						m_server->Driver(false);
						m_server->Hook(true);
						return false;
					}

				}
	}*/
	if (m_videodriver->IsMirrorDriverActive())
	{
		// This should normal not happen
		vnclog.Print(LL_INTERR, VNCLOG("Use active Mirror driver\n"));
		m_videodriver->StartMirroring();
		m_hookdriver=true;
		m_hookdll=false;
	}
	else // no drivers where active, so start the mirror driver
	{
		Checkmonitors();
		nr_monitors=GetNrMonitors();
		if (nr_monitors==1)
		{
			m_ScreenOffsetx=mymonitor[0].offsetx;
			m_ScreenOffsety=mymonitor[0].offsety;
			m_videodriver->Activate_video_driver(true,mymonitor[0].offsetx,mymonitor[0].offsety,mymonitor[0].Width,mymonitor[0].Height);
		}
		if (nr_monitors>1)
		{
			m_ScreenOffsetx=mymonitor[2].offsetx;
			m_ScreenOffsety=mymonitor[2].offsety;
			m_videodriver->Activate_video_driver(true,mymonitor[2].offsetx,mymonitor[2].offsety,mymonitor[2].Width,mymonitor[2].Height);
		}
		

		vnclog.Print(LL_INTERR, VNCLOG("Start Mirror driver\n"));
		m_videodriver->StartMirroring();
		m_hookdriver=true;
		m_hookdll=false;
	}
	// check if driver has mapped the shared memory
	if (!m_videodriver->driver_succes) 
	{
		vnclog.Print(LL_INTERR, VNCLOG("Start Mirror driver Failed\n"));
		vnclog.Print(LL_INTERR, VNCLOG("Using non driver mode\n"));
		if (m_videodriver!=NULL) delete m_videodriver;
		m_videodriver=NULL;
		// If driver selected and fialed to start default to hookdll
		// 
		m_hookdriver=false;
		m_hookdll=true;
		// sf@2002 - Necessary for the following InitHookSettings() call
		// Remember old states
		DriverWantedSet=true;
		DriverWanted=m_server->Driver();
		HookWanted=m_server->Hook();
		m_server->Driver(false);
		m_server->Hook(true);
		return false;
	}
	
	if (m_videodriver->driver_succes)
	{
		vnclog.Print(LL_INTERR, VNCLOG("Driver Used\n"));
		if (!m_videodriver->driver_succes)
		{
			vnclog.Print(LL_INTERR, VNCLOG("Unable to map memory\n"));
			delete m_videodriver;
			m_videodriver=NULL;
			// If driver selected and fialed to start default to hookdll
			// 
			m_hookdriver=false;
			m_hookdll=true;
			return false;
		}
		vnclog.Print(LL_INTERR, VNCLOG("Shared memory mapped\n"));
		InvalidateRect(NULL,NULL,TRUE);

		return true;
	}
	return true;
}


// Modif rdv@2002 - v1.1.x - videodriver
void vncDesktop::ShutdownVideoDriver()
{
	if(OSVersion()!=1) return;
	if (m_videodriver==NULL) return;
	if (m_videodriver!=NULL)
	{
		delete m_videodriver;
		m_videodriver=NULL;
	}
	StopDriverWatches=true;
}

#endif
//
// This proc sets the Desktop update handling params depending
// on tranitted settings and internal constraints.
// It is called from several places (InitHookSettings(), SetHookings()
// and from Desktop thread loop).
// 
void vncDesktop::SethookMechanism(BOOL hookall,BOOL hookdriver)
{
	m_hookswitch=false;
	// Force at least one updates handling method !
	if (!hookall && !hookdriver && !m_server->PollFullScreen())
	{
		hookall = TRUE;
		m_server->PollFullScreen(TRUE);
	}

	// 9,x case 
	vnclog.Print(LL_INTERR, VNCLOG("SethookMechanism called\r\n"));
	if(OSVersion()==4 || OSVersion()==5)
	{
		m_hookdriver=false;//(user driver updates)
		m_hookdll=false;//(use hookdll updates)
		if (hookall || hookdriver) m_hookdll=true;
		//always try to stop the ddihook before starting
		StartStopddihook(false);ddihook=false;
		if (hookdriver) StartStopddihook(true);
		// same for the hookdll
		BOOL old_On_Off_hookdll=On_Off_hookdll;
		if (hookdriver || hookall) On_Off_hookdll=true;
		else On_Off_hookdll=false;
		if (old_On_Off_hookdll!=On_Off_hookdll) Hookdll_Changed=true;
		else Hookdll_Changed=false;
	}
	// W2k-XP case
	else if(OSVersion()==1)
	{
		// sf@2002 - We forbid hoodll and hookdriver at the same time (pointless and high CPU load)
		if (!hookall && !hookdriver) {m_hookdll=false;m_hookdriver=false;}
		if (hookall && hookdriver) {m_hookdll=false;m_hookdriver=true;}
		if (hookall && !hookdriver) {m_hookdll=true;m_hookdriver=false;}
		if (!hookall && hookdriver) {m_hookdll=false;m_hookdriver=true;}

		BOOL old_On_Off_hookdll=On_Off_hookdll;
		if (m_hookdll) On_Off_hookdll=true;
		else On_Off_hookdll=false;
		if (old_On_Off_hookdll!=On_Off_hookdll) Hookdll_Changed=true;
		else Hookdll_Changed=false;
		
		vnclog.Print(LL_INTERR, VNCLOG("Sethook_restart_wanted hook=%d driver=%d \r\n"),m_hookdll,m_hookdriver);
		if (Hookdll_Changed)
			vnclog.Print(LL_INTERR, VNCLOG("Hookdll status changed \r\n"));

		if ((m_hookdriver && !VideoBuffer()) || (!m_hookdriver && VideoBuffer()))
		{
			m_hookswitch=true;
			vnclog.Print(LL_INTERR, VNCLOG("Driver Status changed\r\n"));
		}
	}
	else //NT4
	{
		if (!hookall && !hookdriver) {m_hookdll=false;m_hookdriver=false;}
		if (hookall && hookdriver) {m_hookdll=true;m_hookdriver=false;}
		if (hookall && !hookdriver) {m_hookdll=true;m_hookdriver=false;}
		if (!hookall && hookdriver) {m_hookdll=false;m_hookdriver=false;}
		BOOL old_On_Off_hookdll=On_Off_hookdll;
		if (m_hookdll) On_Off_hookdll=true;
		else On_Off_hookdll=false;
		if (old_On_Off_hookdll!=On_Off_hookdll) Hookdll_Changed=true;
		else Hookdll_Changed=false;
	}

}
void vncDesktop::StartStopddihook(BOOL enabled)
{
	if (enabled)
	{
		STARTUPINFO ssi;
		PROCESS_INFORMATION ppi;
		m_hddihook=NULL;
		char szCurrentDir[MAX_PATH];
		if (GetModuleFileName(NULL, szCurrentDir, MAX_PATH))
		{
			char* p = strrchr(szCurrentDir, '\\');
			if (p == NULL) return;
			*p = '\0';
			strcat (szCurrentDir,"\\16bithlp.exe");
		}
		// Add ddi hook
		ZeroMemory( &ssi, sizeof(ssi) );
		ssi.cb = sizeof(ssi);
		// Start the child process. 
		if( !CreateProcess( NULL,szCurrentDir, NULL,NULL,FALSE,NULL,NULL,NULL,&ssi,&ppi ) ) 
		{
			vnclog.Print(LL_INTERR, VNCLOG("set ddihooks Failed\n"));
			ddihook=false;
		}
		else
		{
			vnclog.Print(LL_INTERR, VNCLOG("set ddihooks OK\n"));
			ddihook=true;
			WaitForInputIdle(ppi.hProcess, 10000);
			m_hddihook=ppi.hProcess;
		}
	}
	else
	{
		if (m_hddihook != NULL)
		{
			TerminateProcess(m_hddihook,0);
			m_hddihook=NULL;
			ddihook=false;
		}
	}

}

void vncDesktop::StartStophookdll(BOOL enabled)
{
	if (enabled)
	{
		if (SetHooks)
		{
			if (!SetHooks(
				GetCurrentThreadId(),
				RFB_SCREEN_UPDATE,
				RFB_COPYRECT_UPDATE,
				RFB_MOUSE_UPDATE, ddihook
				))
			{
				vnclog.Print(LL_INTERR, VNCLOG("failed to set system hooks\n"));
				// Switch on full screen polling, so they can see something, at least...
				m_server->PollFullScreen(TRUE);
				m_hookinited = FALSE;
			} 
			else 
			{
				vnclog.Print(LL_INTERR, VNCLOG("set hooks OK\n"));
				m_hookinited = TRUE;
			}
		}
		// Start up the keyboard and mouse filters
		if (SetKeyboardFilterHook) SetKeyboardFilterHook(m_server->LocalInputsDisabled());
		if (SetMouseFilterHook) SetMouseFilterHook(m_server->LocalInputsDisabled());
	}
	else if (m_hookinited)
	{
		if (UnSetHooks)
		{
		if(!UnSetHooks(GetCurrentThreadId()) )
			vnclog.Print(LL_INTERR, VNCLOG("Unsethooks Failed\n"));
		else vnclog.Print(LL_INTERR, VNCLOG("Unsethooks OK\n"));
		}
		
	}
}


//
//
//
void vncDesktop::InitHookSettings()
{
	SethookMechanism(m_server->Hook(),m_server->Driver());
}
